﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using COFE.Core;
using COFE.Core.Lister;
using COFE.Core.Filter;
using NUnit.Framework;
using COFE.Core.Entry;

namespace COFE.Core.Profile
{
    public class BasicPathParser : IPathParser
    {
        #region Constructor

        public BasicPathParser()
        {
        }

        #endregion

        #region Methods

        protected IFileSystemInfo parseUsingParsePath(IUserProfile profile, string path, string listerKey, EntryConstructionFlags flags)
        {
            //Convert web path to disk path.
            path = path.Replace('/', '\\');
            //Trim the first slash, e.g. "\Temp\Test.txt" to "Temp\Test.txt"
            path = path.TrimStart('\\').TrimEnd('\\');

            if (listerKey == null)
                flags = flags | EntryConstructionFlags.VolumeKeyNotDefined;

            IVolume volume;

            if (listerKey == null)
            {
                listerKey = ProfileRootDirectoryLister.ProfileRootListerKey;
                volume = COFEShared.VolumeRegistrar.Volumes[listerKey];
            }
            else volume = profile.Volumes.FindRegisteredVolume(listerKey);
            
            
            if (volume == null)
                throw new KeyNotFoundException(listerKey + " is not registered.");
            IPrimaryDirectoryLister dirLister = volume == null ? null :
                volume.CacheDirectoryLister as IPrimaryDirectoryLister;
            if (dirLister == null)
                throw new ArgumentException(listerKey + " is Secondary directory lister.");
            IFileSystemInfo retVal = dirLister.ParsePath(path, flags);

            if (retVal != null)
                return retVal;

            //Iterative parse logic

            if ((flags & EntryConstructionFlags.LookupIfParseFailed) != 0)
                return lookupUsingParsePath(profile, path, listerKey, flags);

            return null;
        }

        #region lookupParsePath



        private IFileSystemInfo lookupUsingParsePath(IUserProfile profile, string path, string listerKey, EntryConstructionFlags flags)
        {
            return TryLookupPath(profile, path, listerKey, flags);
        }

        public IFileSystemInfo TryLookupPath(IUserProfile profile, string path, string listerKey, EntryConstructionFlags flags = EntryConstructionFlags.DefaultWithLookup)
        {
            foreach (IDirectoryInfo rootDir in profile.Volumes.RootDirectories)
                if (listerKey == null || rootDir.ListerKey.Equals(listerKey))
                {
                    IFileSystemInfo retVal = TryLookupPath(rootDir, path, flags);
                    if (retVal != null)
                        return retVal;
                }
            return null;
        }

        public IFileSystemInfo TryLookupPath(IUserProfile profile, string path, EntryConstructionFlags flags = EntryConstructionFlags.DefaultWithLookup)
        {
            return TryLookupPath(profile, path, null, flags);
        }

        internal IFileSystemInfo TryLookupPath(IDirectoryInfo parentDir, string relativePath, EntryConstructionFlags flags)
        {
            try
            {
                return LookupPath(parentDir, relativePath, flags);
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        internal IFileSystemInfo LookupPath(IDirectoryInfo parentDir, string relativePath, EntryConstructionFlags flags)
        {
            string[] pathSplit = relativePath.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
            return LookupPath(parentDir, pathSplit, flags);
        }

        internal static IFileSystemInfo LookupPath(IDirectoryInfo parentDir, string[] pathSplit, EntryConstructionFlags flags)
        {
            if (pathSplit.Length == 0)
                return parentDir;

            for (int i = 0; i < pathSplit.Length - 1; i++)
            {
                parentDir = (IDirectoryInfo)lookup(parentDir, pathSplit[i], flags);
                if (parentDir == null)
                    throw new System.IO.FileNotFoundException();
            }
            IFileSystemInfo retVal = lookup(parentDir, pathSplit[pathSplit.Length - 1], flags);
            if (retVal == null)
            {
                parentDir.Refresh();
                retVal = lookup(parentDir, pathSplit[pathSplit.Length - 1], flags);
                if (retVal == null)
                    throw new System.IO.FileNotFoundException();
            }
            return (FileSystemInfoExA)retVal;
        }



        #region lookup (Used by LookupPath)
        private static IFileSystemInfo lookup(IDirectoryInfo parent, string itemName, bool refresh, EntryConstructionFlags flags)
        {
            if (refresh)
                parent.Refresh();
             
            foreach (IFileSystemInfo item in (parent as IInternalDirectoryInfoExA).EnumerateFSIs<IFileSystemInfo>(
                new IEntryFilter[] { },
                new IOptionFilter[] { new SearchOptionOptionFilter(SearchOptionEx.TopDirectoryOnly) }, flags))
            {
                if (String.Equals(item.Name, itemName, StringComparison.InvariantCultureIgnoreCase))
                    return item;

                if (String.Equals(item.Label, itemName, StringComparison.InvariantCultureIgnoreCase))
                    return item;
            }

            return null;
        }

        private static IFileSystemInfo lookup(IDirectoryInfo parent, string itemName, EntryConstructionFlags flags)
        {
            IFileSystemInfo item = lookup(parent, itemName, false, flags);
            if (item == null) item = lookup(parent, itemName, true, flags);
            return item;
        }
        #endregion


        #endregion

        public virtual IFileSystemInfo TryParsePath(IUserProfile profile, string path, ParsableType type,
            EntryConstructionFlags flags = EntryConstructionFlags.DefaultWithLookup)
        {
            Assert.IsNotNull(profile);

            switch (type)
            {
                case ParsableType.ParsePath:
                case ParsableType.ParsePathLookup:
                    string key, parsePath;
                    if (COFEPath.ParseFullParseName(path, out key, out parsePath))
                    {
                        return type == ParsableType.ParsePath ? parseUsingParsePath(profile, parsePath, key, flags) :
                            lookupUsingParsePath(profile, parsePath, key, flags);
                    }
                    else throw new ArgumentException(path + " is not a valid parseName.");

                default: throw new NotSupportedException(type.ToString());
            }
        }

        public virtual bool Exists(IUserProfile profile, string path, ParsableType type, out bool isFolder)
        {
            Assert.IsNotNull(profile);
            if (type == ParsableType.ParsePath)
            {
                string listerKey, parsePath;
                if (COFEPath.ParseFullParseName(path, out listerKey, out parsePath))
                    if (!String.IsNullOrEmpty(listerKey))
                    {
                        var dirListerInfo = profile.Volumes.FindRegisteredVolume(listerKey);
                        if (dirListerInfo == null)
                            throw new ArgumentException("ListerKey");
                        if (dirListerInfo.DirectoryLister is IPrimaryDirectoryLister2)
                        {
                            var dirLister = dirListerInfo.DirectoryLister as IPrimaryDirectoryLister2;
                            return dirLister.Exists(parsePath, out isFolder);
                        }
                    }
            }

            var foundEntry = TryParsePath(profile, path, type);
            isFolder = foundEntry is IDirectoryInfo ? true : false;
            return foundEntry != null;
        }


        #endregion

        #region Data

        #endregion

        #region Public Properties

        public virtual ParsableType SupportedParseType
        {
            get { return ParsableType.ParsePath | ParsableType.ParsePathLookup; }
        }

        #endregion





    }
}
